#pragma once
#include<iostream>
#include<string>
#include<unordered_map>
#include<functional>
#include<sstream>
#include"Logger.hpp"

static const std::string linesep ="\r\n";
static const std::string linersep1 =" ";
static const std::string linersep2 =": ";
static const std::string webroot="./wwwroot";
static const std::string defaulthome="index.html";
static const std::string html_404 = "404.html";
static const std::string suffixsep = ".";
static const std::string argssep = "?";

//定制http协议

class HttpRequest
{
private:
    std::string ReadOneLine(std::string &reqstr,bool *status)
    {
        auto pos=reqstr.find(linesep);
        if(pos == std::string::npos)
        {
            return std::string();
            *status=false;
        }
        auto line=reqstr.substr(0,pos);
        reqstr.erase(0,pos+linesep.size());
        return line;
    }
    void ReadReqLine(std::string &reqline)
    {
        std::stringstream ss(reqline);
        ss >> _method >>_uri >> _httpversion;
    }
    void BuildKV(std::string &line,std::string *k,std::string *v)
    {
        auto pos=line.find(linersep2);
        if(pos==std::string::npos)
        {
            *k=*v=std::string();
            return;
        }
        *k=line.substr(0,pos);
        *v=line.substr(pos+linersep2.size());
    }
public:
    HttpRequest(){}
    void Serialize()//序列化不做，浏览器做
    {}
    bool Deserialize(std::string &reqstr)
    {
        bool status=true;
        std::string reqline = ReadOneLine(reqstr,&status);
        if(!status)
            return false;

        ReadReqLine(reqline);//读首行 _method _uri _httpvision

        // LOG(Loglevel::DEBUG)<<reqline;

        // LOG(Loglevel::DEBUG) << "_method: "<<_method;
        // LOG(Loglevel::DEBUG) << "_uri: "<<_uri;
        // LOG(Loglevel::DEBUG) << "_httpversion "<<_httpversion;
       
        while(true)
        {
            status=true;
            reqline=ReadOneLine(reqstr,&status);
            if(status&&!reqline.empty())
            {
                std::string k,v;
                BuildKV(reqline,&k,&v);
                if(k.empty()||v.empty())
                    continue;
                _req_headers.insert({k,v});
            }
            else if(status)
            {
                _blank_line=linesep;
                break;
            }
            else{
                break;
            }
        }

        _path = webroot;
        _path +=_uri;
        if(_uri=="/")
        {
            _path+=defaulthome;
        }
        LOG(Loglevel::DEBUG)<<"_path: "<<_path;

        if(_method == "GET")
        {
            auto pos=_path.find(argssep);
            if(pos !=std::string::npos)
            {
                //不用定义新的string args变量，直接用_req_body
                _req_body=_path.substr(pos+argssep.size());
                _path = _path.substr(0,pos);
            }
            LOG(Loglevel::DEBUG) << "_path:" << _path;
            LOG(Loglevel::DEBUG) << "_args:" << _req_body;
        }
        else if(_method == "POST")
        {
            _req_body = reqstr;
        }

        return true;
    }
    std::string Path()
    {
        return _path;
    }
    void SetPath(const std::string &path)
    {
        _path = path;
    }
    std::string Body()
    {
        return _req_body;
    }
    //获取资源的文件后缀
    std::string Suffix()
    {
        //_path : /index.html
        if (_path.empty())
            return std::string();
        else
        {
            auto pos = _path.rfind(suffixsep);
            if (pos == std::string::npos)
                return std::string();
            else
                return _path.substr(pos); // .html
        }
    }

    ~HttpRequest(){}
private:
    std::string _method;
    std::string _uri;
    std::string _httpversion;
    std::unordered_map<std::string,std::string> _req_headers;
    std::string _blank_line;
    std::string _req_body;

    std::string _path; // 真正的要访问的资源的路径！
};

class HttpResponse
{
private:
    //由状态码到状态描述
    std::string Code2Desc(int code)
    {
        switch (code)
        {
        case 200:
            return "OK";
        case 400:
            return "Bad Request";
        case 404:
            return "Not Found";
        case 301:
            return "Moved Permanently";
        case 302:
            return "See Other";
        case 307:
            return "Temporary Redirect";
        default:
            return "";
        }
    }
public:
    HttpResponse():_httpversion("HTTP/1.1"),_black_line(linesep)
    {}
    std::string Serialize()
    {
        std::string respstr=_httpversion+linersep1+std::to_string(_code)+\
                linersep1 + _desc+linesep;

        //是否有有效载荷
        if(!_resp_body.empty())
        {
            std::string len=std::to_string(_resp_body.size());
            SetHeader("Content-Length",len);
        }     

        for(auto&elem:_resp_headers)
        {
            std::string line=elem.first + linersep2 +elem.second +linesep;
            respstr +=line;
        }
        for (auto &cookie : _cookie)
        {
            respstr += cookie;
            respstr += linesep;
        }
        respstr += _black_line;

        respstr += _resp_body;

        return respstr;
    } 
    bool ReadContent(const std::string&path)
    {
        std::ifstream file(path,std::ios::binary);
        if(!file.is_open())
        {
            throw std::runtime_error("无法打开文件: "+path);
        }

        //获取文件大小
        file.seekg(0,std::ios::end);
        std::streamsize fileSize=file.tellg();
        file.seekg(0,std::ios::beg);

        _resp_body.resize(static_cast<size_t>(fileSize));

        //读取文件内容
        file.read(&_resp_body[0],fileSize);

        //是否读取所有数据
        if(!file)
        {
            throw std::runtime_error("读取文件时出错");
        }

        file.close();
        return true;
    }
    void SetCode(int code)
    {
        if(code >= 100 && code < 600)
        {
            _code=code;
            _desc=Code2Desc(_code);
        }
        else
        {
            LOG(Loglevel::DEBUG) << "非法的状态码: " << code;
        }
    }
    bool SetHeader(const std::string &key, const std::string &value)
    {
        _resp_headers[key] = value;
        return true;
    }
    void SetCookie(const std::string &key, const std::string &value)
    {
        std::string cookie = key;
        cookie += ": ";
        cookie += value;
        _cookie.push_back(cookie);
    }
    ~HttpResponse(){}
private:
    std::string _httpversion;
    int _code;
    std::string _desc;
    std::unordered_map<std::string,std::string> _resp_headers;
    std::vector<std::string> _cookie;
    std::string _black_line;
    std::string _resp_body;
};

using func_t = std::function<HttpResponse(HttpRequest &)>;

class Http
{
//路由功能
private:
    std::unordered_map<std::string,func_t> _handlers;

public:
    std::string Suffix2Desc(const std::string &suffix)
    {
        if (suffix == ".html")
            return "text/html";
        else if (suffix == ".css")
            return "text/css";
        else if (suffix == ".js")
            return "application/x-javascript";
        else if (suffix == ".png")
            return "image/png";
        else if (suffix == ".jpg")
            return "image/jpeg";
        else if (suffix == ".txt")
            return "text/plain";
        else
            return "text/html";
    }
public:
    Http(){}
    void Register(const std::string &action, func_t handler)
    {
        std::string key=webroot;
        key +=action;
        _handlers[key]=handler;
    }
    std::string HanderRequert(std::string &requeststr)
    {
        std::string respstr;
        HttpRequest req;
        std::cout << requeststr;
        if(req.Deserialize(requeststr))
        {
            HttpResponse resp;
            // 1. 交互式处理
            //./wwwroot/login
            std::string target=req.Path();
            if(_handlers.find(target)!=_handlers.end())
            {
                resp = _handlers[target](req);
            }
            else // 2. 处理静态网页资源！./wwwroot/index.html
            {
                if(resp.ReadContent(req.Path()))
                {
                    std::string suffix = req.Suffix();
                    std::string type =Suffix2Desc(suffix);
                    resp.SetHeader("Content-Type",type);
                    resp.SetCode(200);
                }
                else
                {
                    resp.SetCode(301);
                    resp.SetHeader("Location","/404.html");
                }
            }
            respstr =resp.Serialize();
        }
        return respstr;
    }
    ~Http(){}
};